#include "bglfile.h"

BGLFile::BGLFile(wxString filename, const wxString &source_charset,
		const wxString &target_charset) {
	sourceCharset = source_charset;
	targetCharset = target_charset;

	phonetic_u_reg.Compile(_T("(Pinyin</I>=)(.*)(\xFC)"), wxRE_ADVANCED); // Pinyin=lü3  
	numEntries = 0;

	file_name = filename;
}

BGLFile::~BGLFile() {
	close();
}

void BGLFile::close() {
    raw_file.Close();
	delete gzipstream;
}

bool BGLFile::open() {
	filestream = new wxFFileInputStream(file_name);
	gzipstream = new wxZlibInputStream(filestream);
    raw_file.Open(file_name+_T(".raw.txt"), _T("w+"));

	// First four bytes: BGL signature 0x12340001 or 0x12340002 (big-endian)
	unsigned char header[6];
	filestream->Read(header, 6);
	if (memcmp(header, "\x12\x34\x00", 3) || header[3] == 0|| header[3]> 2) {
		wxPrintf(_("BGL file format error!\n"));
		return false;
	}

	/* Calculate position of gz header */

	int i = header[4] << 8| header[5];
	if (i < 6) {
		wxPrintf(_("BGL file gz header error!\n"));
		return false;
	}

	if (wxInvalidOffset==filestream->SeekI(i)) {
		wxPrintf(_("BGL file gz header error!\n"));
		return false;
	}

	return true;
}

bool BGLFile::readBlock(bgl_block &block) {
	if (!gzipstream->CanRead()) {
		return false;
	}

	block.length = read_number(1);

	block.type = block.length & 0xf;

	if (block.type == 4)
		return false; // end of file marker

	block.length >>= 4;
	block.length = block.length < 4 ? read_number(block.length + 1)
			: block.length - 4;

	if (block.length) {
		block.data = (char *)malloc(block.length);
		gzipstream->Read(block.data, block.length);

		// write raw file
		raw_file.Write(block.data, block.length);
	}

	return true;
}

unsigned int BGLFile::read_number(int bytes) {
	unsigned char buf[4];
	unsigned val = 0;

	if (bytes< 1 || bytes > 4 )return (0);

	gzipstream->Read(buf, bytes);
	for(int i=0;i<bytes;i++) {
		val= (val << 8) | buf[i];
	}

	return val;
}

bool BGLFile::read_info() {
    bgl_block block;
    unsigned int len, pos;
    unsigned int type;
    wxString filename;
    
    while (readBlock(block ) ) {
        char buf[block.length];

        switch (block.type) {
        case 0: // charset
            if (8 == block.data[0]) {
                type = (unsigned int)block.data[2];
                if (type > 64)
                    type -= 65;
                defaultCharset = bgl_charset[type];
            }
            break;
        case 2: // resource
            pos = 0;
            len = (unsigned char)block.data[pos++];
            filename = wxString::From8BitData(block.data+pos, len);
            if (filename != _T("8EAF66FD.bmp")&& filename != _T("C2EEF3F6.html")) {
                filename = _T("res/") + filename;
                wxPrintf(_T("----------- image ------------\n"));
                pos += len;
                wxFFile ifile(filename.c_str(), _T("w+"));
                ifile.Write(block.data + pos, block.length -pos);
            }
            break;

        case 3: // dict info
            pos = 2;
            switch (block.data[1]) {
            case 1:
                title = wxString::From8BitData(&block.data[2],
                        block.length-2);
                break;
            case 2:
                author = wxString::From8BitData(
                        &block.data[2], block.length-2);
                break;
            case 3:
                email = wxString::From8BitData(&block.data[2],
                        block.length-2);
                break;
            case 4:
                copyright = wxString::From8BitData(
                        &block.data[2], block.length-2);
                break;
            case 7:
                sourceLang
                        = bgl_language[(unsigned char)(block.data[5])];
                break;
            case 8:
                targetLang
                        = bgl_language[(unsigned char)(block.data[5])];
                break;
            case 9:
                memset(buf, 0, block.length);

                for (unsigned int a=0; a<block.length-2; a++) {
                    if (block.data[pos] == '\r') {
                    } else if (block.data[pos] == '\n') {
                        strcat(buf, "<br>");
                    } else {
                        strncat(buf, &block.data[pos], 1);
                    }
                    pos++;
                }
                description = wxString::From8BitData(buf);
                break;
            case 26:
                type = (unsigned int)block.data[2];
                if (type > 64)
                    type -= 65;
                if (sourceCharset.empty())
                    sourceCharset = bgl_charset[type];
                break;
            case 27:
                type = (unsigned int)block.data[2];
                if (type > 64)
                    type -= 65;
                if (targetCharset.empty())
                    targetCharset = bgl_charset[type];
                break;
            default:
                break;
            } // end case3 sub switch
            
            break; // case 3:  // dict info

        default:
            break;
        } // end switch
        
        if (block.length)
            free(block.data);
    } // end loop
    
    close();
    open();

    title = wxString(title.To8BitData(), wxCSConv(defaultCharset));
    author = wxString(author.To8BitData(), wxCSConv(defaultCharset));
    email = wxString(email.To8BitData(), wxCSConv(defaultCharset));
    copyright = wxString(copyright.To8BitData(), wxCSConv(defaultCharset));
    description = wxString(description.To8BitData(), wxCSConv(targetCharset));
    
    return true;
}

bgl_entry BGLFile::readEntry() {
	bgl_entry entry;
	entry.headword = _T("");

	bgl_block block;
	unsigned int len, pos;
	unsigned int type;
	wxString temp;

	while (readBlock(block ) ) {
		char buf[block.length];

		switch (block.type) {
		case 1: // words, must return when 
		case 10: // words begin
			numEntries++;
			pos = 0;

			// Headword
			len = (unsigned char)block.data[pos++];
			
			entry.headword = wxString(&block.data[pos], wxCSConv(sourceCharset), len);
			pos = pos + len;

			// Definition
			len = (unsigned char)block.data[pos++] << 8;
			len |= (unsigned char)block.data[pos++];

			for (unsigned int a=0; a<len; a++) {
				if ( (unsigned char)block.data[pos] == 0x0a) { // new line feed
					entry.definition = entry.definition + _T("<br>");
					pos++;
				} else if ( (unsigned char)block.data[pos] < 0x20) { // < space (printable char)
					if (a <= (len - 3)&& (block.data[pos] == 0x14)
							&& (block.data[pos+1] == 0x02)) {
						int index = (unsigned char)block.data[pos+2] - 0x30;
						if (index <= 10) {
							entry.definition = _T("<partofspeech>")
							+ partOfSpeech[index]
							+ _T("</partofspeech> ")
							+ entry.definition;
						}
						pos += len - a;
						break;
					} else if (block.data[pos] == 0x14) {
						pos++;
					} else {
						entry.definition += wxString::From8BitData(&block.data[pos++],1);
					}
				} else
					entry.definition += wxString::From8BitData(&block.data[pos++],1);
			} // end for
			
			while(phonetic_u_reg.Replace(&entry.definition, _T("\\1\\2v")) > 0) {
			    // continue;
			}
			entry.definition = wxString(entry.definition.To8BitData(), wxCSConv(targetCharset));
			
			if (block.length)
				free(block.data);
			return entry;

			break;
			
		default:
		    break;
		} // end switch (block.type) {

		if (block.length)
			free(block.data);
	} // end while
	
	// wxPrintf(entry.headword);
	return entry;
}
